import { useCurrentUser } from '@/composables/auth/useCurrentUser'
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems'
import { useExternalLink } from '@/composables/useExternalLink'
import { useModelSelectorDialog } from '@/composables/useModelSelectorDialog'
import {
  DEFAULT_DARK_COLOR_PALETTE,
  DEFAULT_LIGHT_COLOR_PALETTE
} from '@/constants/coreColorPalettes'
import { tryToggleWidgetPromotion } from '@/core/graph/subgraph/proxyWidgetUtils'
import { t } from '@/i18n'
import {
  LGraphEventMode,
  LGraphGroup,
  LGraphNode,
  LiteGraph,
  SubgraphNode
} from '@/lib/litegraph/src/litegraph'
import type { Point } from '@/lib/litegraph/src/litegraph'
import { useAssetBrowserDialog } from '@/platform/assets/composables/useAssetBrowserDialog'
import { createModelNodeFromAsset } from '@/platform/assets/utils/createModelNodeFromAsset'
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
import { useSettingStore } from '@/platform/settings/settingStore'
import { buildSupportUrl } from '@/platform/support/config'
import { useTelemetry } from '@/platform/telemetry'
import type { ExecutionTriggerSource } from '@/platform/telemetry/types'
import { useToastStore } from '@/platform/updates/common/toastStore'
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
import type { ComfyWorkflow } from '@/platform/workflow/management/stores/workflowStore'
import {
  useCanvasStore,
  useTitleEditorStore
} from '@/renderer/core/canvas/canvasStore'
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
import { selectionBounds } from '@/renderer/core/layout/utils/layoutMath'
import { api } from '@/scripts/api'
import { app } from '@/scripts/app'
import { useDialogService } from '@/services/dialogService'
import { useLitegraphService } from '@/services/litegraphService'
import type { ComfyCommand } from '@/stores/commandStore'
import { useExecutionStore } from '@/stores/executionStore'
import { useHelpCenterStore } from '@/stores/helpCenterStore'
import { useNodeOutputStore } from '@/stores/imagePreviewStore'
import { useQueueSettingsStore, useQueueStore } from '@/stores/queueStore'
import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore'
import { useSubgraphStore } from '@/stores/subgraphStore'
import { useBottomPanelStore } from '@/stores/workspace/bottomPanelStore'
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
import { useSearchBoxStore } from '@/stores/workspace/searchBoxStore'
import { useWorkspaceStore } from '@/stores/workspaceStore'
import {
  getAllNonIoNodesInSubgraph,
  getExecutionIdsForSelectedNodes
} from '@/utils/graphTraversalUtil'
import { filterOutputNodes } from '@/utils/nodeFilterUtil'
import {
  ManagerUIState,
  useManagerState
} from '@/workbench/extensions/manager/composables/useManagerState'
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'

import { useWorkflowTemplateSelectorDialog } from './useWorkflowTemplateSelectorDialog'

const { isActiveSubscription, showSubscriptionDialog } = useSubscription()

const moveSelectedNodesVersionAdded = '1.22.2'

export function useCoreCommands(): ComfyCommand[] {
  const workflowService = useWorkflowService()
  const workflowStore = useWorkflowStore()
  const dialogService = useDialogService()
  const colorPaletteStore = useColorPaletteStore()
  const firebaseAuthActions = useFirebaseAuthActions()
  const toastStore = useToastStore()
  const canvasStore = useCanvasStore()
  const executionStore = useExecutionStore()
  const telemetry = useTelemetry()
  const { staticUrls, buildDocsUrl } = useExternalLink()

  const bottomPanelStore = useBottomPanelStore()

  const { getSelectedNodes, toggleSelectedNodesMode } =
    useSelectedLiteGraphItems()
  const getTracker = () => workflowStore.activeWorkflow?.changeTracker

  const moveSelectedNodes = (
    positionUpdater: (pos: Point, gridSize: number) => Point
  ) => {
    const selectedNodes = getSelectedNodes()
    if (selectedNodes.length === 0) return

    const gridSize = useSettingStore().get('Comfy.SnapToGrid.GridSize')
    selectedNodes.forEach((node) => {
      node.pos = positionUpdater(node.pos, gridSize)
    })
    app.canvas.state.selectionChanged = true
    app.canvas.setDirty(true, true)
  }

  const commands = [
    {
      id: 'Comfy.NewBlankWorkflow',
      icon: 'pi pi-plus',
      label: 'New Blank Workflow',
      menubarLabel: 'New',
      category: 'essentials' as const,
      function: async () => {
        const previousWorkflowHadNodes = app.graph._nodes.length > 0
        await workflowService.loadBlankWorkflow()
        telemetry?.trackWorkflowCreated({
          workflow_type: 'blank',
          previous_workflow_had_nodes: previousWorkflowHadNodes
        })
      }
    },
    {
      id: 'Comfy.OpenWorkflow',
      icon: 'pi pi-folder-open',
      label: 'Open Workflow',
      menubarLabel: 'Open',
      category: 'essentials' as const,
      function: () => {
        app.ui.loadFile()
      }
    },
    {
      id: 'Comfy.LoadDefaultWorkflow',
      icon: 'pi pi-code',
      label: 'Load Default Workflow',
      function: async () => {
        const previousWorkflowHadNodes = app.graph._nodes.length > 0
        await workflowService.loadDefaultWorkflow()
        telemetry?.trackWorkflowCreated({
          workflow_type: 'default',
          previous_workflow_had_nodes: previousWorkflowHadNodes
        })
      }
    },
    {
      id: 'Comfy.SaveWorkflow',
      icon: 'pi pi-save',
      label: 'Save Workflow',
      menubarLabel: 'Save',
      category: 'essentials' as const,
      function: async () => {
        const workflow = useWorkflowStore().activeWorkflow as ComfyWorkflow
        if (!workflow) return

        await workflowService.saveWorkflow(workflow)
      }
    },
    {
      id: 'Comfy.PublishSubgraph',
      icon: 'pi pi-save',
      label: 'Publish Subgraph',
      menubarLabel: 'Publish',
      function: async () => {
        await useSubgraphStore().publishSubgraph()
      }
    },
    {
      id: 'Comfy.SaveWorkflowAs',
      icon: 'pi pi-save',
      label: 'Save Workflow As',
      menubarLabel: 'Save As',
      category: 'essentials' as const,
      function: async () => {
        const workflow = useWorkflowStore().activeWorkflow as ComfyWorkflow
        if (!workflow) return

        await workflowService.saveWorkflowAs(workflow)
      }
    },
    {
      id: 'Comfy.ExportWorkflow',
      icon: 'pi pi-download',
      label: 'Export Workflow',
      menubarLabel: 'Export',
      category: 'essentials' as const,
      function: async () => {
        await workflowService.exportWorkflow('workflow', 'workflow')
      }
    },
    {
      id: 'Comfy.ExportWorkflowAPI',
      icon: 'pi pi-download',
      label: 'Export Workflow (API Format)',
      menubarLabel: 'Export (API)',
      function: async () => {
        await workflowService.exportWorkflow('workflow_api', 'output')
      }
    },
    {
      id: 'Comfy.Undo',
      icon: 'pi pi-undo',
      label: 'Undo',
      category: 'essentials' as const,
      function: async () => {
        await getTracker()?.undo?.()
      }
    },
    {
      id: 'Comfy.Redo',
      icon: 'pi pi-refresh',
      label: 'Redo',
      category: 'essentials' as const,
      function: async () => {
        await getTracker()?.redo?.()
      }
    },
    {
      id: 'Comfy.ClearWorkflow',
      icon: 'pi pi-trash',
      label: 'Clear Workflow',
      category: 'essentials' as const,
      function: () => {
        const settingStore = useSettingStore()
        if (
          !settingStore.get('Comfy.ConfirmClear') ||
          confirm('Clear workflow?')
        ) {
          app.clean()
          if (app.canvas.subgraph) {
            // `clear` is not implemented on subgraphs and the parent class's
            // (`LGraph`) `clear` breaks the subgraph structure. For subgraphs,
            // just clear the nodes but preserve input/output nodes and structure
            const subgraph = app.canvas.subgraph
            const nonIoNodes = getAllNonIoNodesInSubgraph(subgraph)
            nonIoNodes.forEach((node) => subgraph.remove(node))
          }
          api.dispatchCustomEvent('graphCleared')
        }
      }
    },
    {
      id: 'Comfy.Canvas.ResetView',
      icon: 'pi pi-expand',
      label: 'Reset View',
      function: () => {
        useLitegraphService().resetView()
      }
    },
    {
      id: 'Comfy.OpenClipspace',
      icon: 'pi pi-clipboard',
      label: 'Clipspace',
      function: () => {
        app.openClipspace()
      }
    },
    {
      id: 'Comfy.RefreshNodeDefinitions',
      icon: 'pi pi-refresh',
      label: 'Refresh Node Definitions',
      category: 'essentials' as const,
      function: async () => {
        await app.refreshComboInNodes()
      }
    },
    {
      id: 'Comfy.Interrupt',
      icon: 'pi pi-stop',
      label: 'Interrupt',
      category: 'essentials' as const,
      function: async () => {
        await api.interrupt(executionStore.activePromptId)
        toastStore.add({
          severity: 'info',
          summary: t('g.interrupted'),
          detail: t('toastMessages.interrupted'),
          life: 1000
        })
      }
    },
    {
      id: 'Comfy.ClearPendingTasks',
      icon: 'pi pi-stop',
      label: 'Clear Pending Tasks',
      category: 'essentials' as const,
      function: async () => {
        await useQueueStore().clear(['queue'])
        toastStore.add({
          severity: 'info',
          summary: t('g.confirmed'),
          detail: t('toastMessages.pendingTasksDeleted'),
          life: 3000
        })
      }
    },
    {
      id: 'Comfy.BrowseTemplates',
      icon: 'pi pi-folder-open',
      label: 'Browse Templates',
      function: () => {
        useWorkflowTemplateSelectorDialog().show()
      }
    },
    {
      id: 'Comfy.Canvas.ZoomIn',
      icon: 'pi pi-plus',
      label: 'Zoom In',
      category: 'view-controls' as const,
      function: () => {
        const ds = app.canvas.ds
        ds.changeScale(
          ds.scale * 1.1,
          ds.element ? [ds.element.width / 2, ds.element.height / 2] : undefined
        )
        app.canvas.setDirty(true, true)
      }
    },
    {
      id: 'Comfy.Canvas.ZoomOut',
      icon: 'pi pi-minus',
      label: 'Zoom Out',
      category: 'view-controls' as const,
      function: () => {
        const ds = app.canvas.ds
        ds.changeScale(
          ds.scale / 1.1,
          ds.element ? [ds.element.width / 2, ds.element.height / 2] : undefined
        )
        app.canvas.setDirty(true, true)
      }
    },
    {
      id: 'Experimental.ToggleVueNodes',
      label: () =>
        `Experimental: ${
          useSettingStore().get('Comfy.VueNodes.Enabled') ? 'Disable' : 'Enable'
        } Nodes 2.0`,
      function: async () => {
        const settingStore = useSettingStore()
        const current = settingStore.get('Comfy.VueNodes.Enabled') ?? false
        await settingStore.set('Comfy.VueNodes.Enabled', !current)
      }
    },
    {
      id: 'Comfy.Canvas.FitView',
      icon: 'pi pi-expand',
      label: 'Fit view to selected nodes',
      menubarLabel: 'Zoom to fit',
      category: 'view-controls' as const,
      function: () => {
        const vueNodesEnabled = useSettingStore().get('Comfy.VueNodes.Enabled')

        if (vueNodesEnabled) {
          // Get nodes from Vue stores
          const canvasStore = useCanvasStore()
          const selectedNodeIds = canvasStore.selectedNodeIds
          const allNodes = layoutStore.getAllNodes().value

          // Get nodes to fit - selected if any, otherwise all
          const nodesToFit =
            selectedNodeIds.size > 0
              ? Array.from(selectedNodeIds)
                  .map((id) => allNodes.get(id))
                  .filter((node) => node != null)
              : Array.from(allNodes.values())

          // Use Vue nodes bounds calculation
          const bounds = selectionBounds(nodesToFit)
          if (!bounds) {
            toastStore.add({
              severity: 'error',
              summary: t('toastMessages.emptyCanvas'),
              life: 3000
            })
            return
          }

          // Convert to LiteGraph format and animate
          const lgBounds = [
            bounds.x,
            bounds.y,
            bounds.width,
            bounds.height
          ] as const
          const setDirty = () => app.canvas.setDirty(true, true)
          app.canvas.ds.animateToBounds(lgBounds, setDirty)
        } else {
          if (app.canvas.empty) {
            toastStore.add({
              severity: 'error',
              summary: t('toastMessages.emptyCanvas'),
              life: 3000
            })
            return
          }
          app.canvas.fitViewToSelectionAnimated()
        }
      }
    },
    {
      id: 'Comfy.Canvas.ToggleLock',
      icon: 'pi pi-lock',
      label: 'Canvas Toggle Lock',
      category: 'view-controls' as const,
      function: () => {
        app.canvas.state.readOnly = !app.canvas.state.readOnly
      }
    },
    {
      id: 'Comfy.Canvas.Lock',
      icon: 'pi pi-lock',
      label: 'Lock Canvas',
      category: 'view-controls' as const,
      function: () => {
        app.canvas.state.readOnly = true
      }
    },
    {
      id: 'Comfy.Canvas.Unlock',
      icon: 'pi pi-lock-open',
      label: 'Unlock Canvas',
      function: () => {
        app.canvas.state.readOnly = false
      }
    },
    {
      id: 'Comfy.Canvas.ToggleLinkVisibility',
      icon: 'pi pi-eye',
      label: 'Canvas Toggle Link Visibility',
      menubarLabel: 'Node Links',
      versionAdded: '1.3.6',

      function: (() => {
        const settingStore = useSettingStore()
        let lastLinksRenderMode = LiteGraph.SPLINE_LINK

        return async () => {
          const currentMode = settingStore.get('Comfy.LinkRenderMode')

          if (currentMode === LiteGraph.HIDDEN_LINK) {
            // If links are hidden, restore the last positive value or default to spline mode
            await settingStore.set('Comfy.LinkRenderMode', lastLinksRenderMode)
          } else {
            // If links are visible, store the current mode and hide links
            lastLinksRenderMode = currentMode
            await settingStore.set(
              'Comfy.LinkRenderMode',
              LiteGraph.HIDDEN_LINK
            )
          }
        }
      })(),
      active: () =>
        useSettingStore().get('Comfy.LinkRenderMode') !== LiteGraph.HIDDEN_LINK
    },
    {
      id: 'Comfy.Canvas.ToggleMinimap',
      icon: 'pi pi-map',
      label: 'Canvas Toggle Minimap',
      menubarLabel: 'Minimap',
      versionAdded: '1.24.1',
      function: async () => {
        const settingStore = useSettingStore()
        await settingStore.set(
          'Comfy.Minimap.Visible',
          !settingStore.get('Comfy.Minimap.Visible')
        )
      },
      active: () => useSettingStore().get('Comfy.Minimap.Visible')
    },
    {
      id: 'Comfy.QueuePrompt',
      icon: 'pi pi-play',
      label: 'Queue Prompt',
      versionAdded: '1.3.7',
      category: 'essentials' as const,
      function: async (metadata?: {
        subscribe_to_run?: boolean
        trigger_source?: ExecutionTriggerSource
      }) => {
        useTelemetry()?.trackRunButton(metadata)
        if (!isActiveSubscription.value) {
          showSubscriptionDialog()
          return
        }

        const batchCount = useQueueSettingsStore().batchCount

        useTelemetry()?.trackWorkflowExecution()

        await app.queuePrompt(0, batchCount)
      }
    },
    {
      id: 'Comfy.QueuePromptFront',
      icon: 'pi pi-play',
      label: 'Queue Prompt (Front)',
      versionAdded: '1.3.7',
      category: 'essentials' as const,
      function: async (metadata?: {
        subscribe_to_run?: boolean
        trigger_source?: ExecutionTriggerSource
      }) => {
        useTelemetry()?.trackRunButton(metadata)
        if (!isActiveSubscription.value) {
          showSubscriptionDialog()
          return
        }

        const batchCount = useQueueSettingsStore().batchCount

        useTelemetry()?.trackWorkflowExecution()

        await app.queuePrompt(-1, batchCount)
      }
    },
    {
      id: 'Comfy.QueueSelectedOutputNodes',
      icon: 'pi pi-play',
      label: 'Queue Selected Output Nodes',
      versionAdded: '1.19.6',
      function: async (metadata?: {
        subscribe_to_run?: boolean
        trigger_source?: ExecutionTriggerSource
      }) => {
        useTelemetry()?.trackRunButton(metadata)
        if (!isActiveSubscription.value) {
          showSubscriptionDialog()
          return
        }

        const batchCount = useQueueSettingsStore().batchCount
        const selectedNodes = getSelectedNodes()
        const selectedOutputNodes = filterOutputNodes(selectedNodes)

        if (selectedOutputNodes.length === 0) {
          toastStore.add({
            severity: 'error',
            summary: t('toastMessages.nothingToQueue'),
            detail: t('toastMessages.pleaseSelectOutputNodes'),
            life: 3000
          })
          return
        }

        // Get execution IDs for all selected output nodes and their descendants
        const executionIds =
          getExecutionIdsForSelectedNodes(selectedOutputNodes)

        if (executionIds.length === 0) {
          toastStore.add({
            severity: 'error',
            summary: t('toastMessages.failedToQueue'),
            detail: t('toastMessages.failedExecutionPathResolution'),
            life: 3000
          })
          return
        }
        useTelemetry()?.trackWorkflowExecution()
        await app.queuePrompt(0, batchCount, executionIds)
      }
    },
    {
      id: 'Comfy.ShowSettingsDialog',
      icon: 'pi pi-cog',
      label: 'Show Settings Dialog',
      versionAdded: '1.3.7',
      category: 'view-controls' as const,
      function: () => {
        dialogService.showSettingsDialog()
      }
    },
    {
      id: 'Comfy.Graph.GroupSelectedNodes',
      icon: 'pi pi-sitemap',
      label: 'Group Selected Nodes',
      versionAdded: '1.3.7',
      category: 'essentials' as const,
      function: () => {
        const { canvas } = app
        if (!canvas.selectedItems?.size) {
          toastStore.add({
            severity: 'error',
            summary: t('toastMessages.nothingToGroup'),
            detail: t('toastMessages.pleaseSelectNodesToGroup'),
            life: 3000
          })
          return
        }
        const group = new LGraphGroup()
        const padding = useSettingStore().get(
          'Comfy.GroupSelectedNodes.Padding'
        )
        group.resizeTo(canvas.selectedItems, padding)
        canvas.graph?.add(group)

        group.recomputeInsideNodes()

        useTitleEditorStore().titleEditorTarget = group
      }
    },
    {
      id: 'Workspace.NextOpenedWorkflow',
      icon: 'pi pi-step-forward',
      label: 'Next Opened Workflow',
      versionAdded: '1.3.9',
      function: async () => {
        await workflowService.loadNextOpenedWorkflow()
      }
    },
    {
      id: 'Workspace.PreviousOpenedWorkflow',
      icon: 'pi pi-step-backward',
      label: 'Previous Opened Workflow',
      versionAdded: '1.3.9',
      function: async () => {
        await workflowService.loadPreviousOpenedWorkflow()
      }
    },
    {
      id: 'Comfy.Canvas.ToggleSelectedNodes.Mute',
      icon: 'pi pi-volume-off',
      label: 'Mute/Unmute Selected Nodes',
      versionAdded: '1.3.11',
      category: 'essentials' as const,
      function: () => {
        toggleSelectedNodesMode(LGraphEventMode.NEVER)
        app.canvas.setDirty(true, true)
      }
    },
    {
      id: 'Comfy.Canvas.ToggleSelectedNodes.Bypass',
      icon: 'pi pi-shield',
      label: 'Bypass/Unbypass Selected Nodes',
      versionAdded: '1.3.11',
      category: 'essentials' as const,
      function: () => {
        toggleSelectedNodesMode(LGraphEventMode.BYPASS)
        app.canvas.setDirty(true, true)
      }
    },
    {
      id: 'Comfy.Canvas.ToggleSelectedNodes.Pin',
      icon: 'pi pi-pin',
      label: 'Pin/Unpin Selected Nodes',
      versionAdded: '1.3.11',
      category: 'essentials' as const,
      function: () => {
        getSelectedNodes().forEach((node) => {
          node.pin(!node.pinned)
        })
        app.canvas.setDirty(true, true)
      }
    },
    {
      id: 'Comfy.Canvas.ToggleSelected.Pin',
      icon: 'pi pi-pin',
      label: 'Pin/Unpin Selected Items',
      versionAdded: '1.3.33',
      function: () => {
        for (const item of app.canvas.selectedItems) {
          if (item instanceof LGraphNode || item instanceof LGraphGroup) {
            item.pin(!item.pinned)
          }
        }
        app.canvas.setDirty(true, true)
      }
    },
    {
      id: 'Comfy.Canvas.Resize',
      icon: 'pi pi-minus',
      label: 'Resize Selected Nodes',
      versionAdded: '',
      function: () => {
        getSelectedNodes().forEach((node) => {
          const optimalSize = node.computeSize()
          node.setSize([optimalSize[0], optimalSize[1]])
        })
        app.canvas.setDirty(true, true)
      }
    },
    {
      id: 'Comfy.Canvas.ToggleSelectedNodes.Collapse',
      icon: 'pi pi-minus',
      label: 'Collapse/Expand Selected Nodes',
      versionAdded: '1.3.11',
      function: () => {
        getSelectedNodes().forEach((node) => {
          node.collapse()
        })
        app.canvas.setDirty(true, true)
      }
    },
    {
      id: 'Comfy.ToggleTheme',
      icon: 'pi pi-moon',
      label: 'Toggle Theme (Dark/Light)',
      versionAdded: '1.3.12',
      function: (() => {
        let previousDarkTheme: string = DEFAULT_DARK_COLOR_PALETTE.id
        let previousLightTheme: string = DEFAULT_LIGHT_COLOR_PALETTE.id

        return async () => {
          const settingStore = useSettingStore()
          const theme = colorPaletteStore.completedActivePalette
          if (theme.light_theme) {
            previousLightTheme = theme.id
            await settingStore.set('Comfy.ColorPalette', previousDarkTheme)
          } else {
            previousDarkTheme = theme.id
            await settingStore.set('Comfy.ColorPalette', previousLightTheme)
          }
        }
      })()
    },
    {
      id: 'Workspace.ToggleBottomPanel',
      icon: 'pi pi-list',
      label: 'Toggle Bottom Panel',
      menubarLabel: 'Bottom Panel',
      versionAdded: '1.3.22',
      category: 'view-controls' as const,
      function: () => {
        bottomPanelStore.toggleBottomPanel()
      },
      active: () => bottomPanelStore.bottomPanelVisible
    },
    {
      id: 'Workspace.ToggleFocusMode',
      icon: 'pi pi-eye',
      label: 'Toggle Focus Mode',
      menubarLabel: 'Focus Mode',
      versionAdded: '1.3.27',
      category: 'view-controls' as const,
      function: () => {
        useWorkspaceStore().toggleFocusMode()
      },
      active: () => useWorkspaceStore().focusMode
    },
    {
      id: 'Comfy.Graph.FitGroupToContents',
      icon: 'pi pi-expand',
      label: 'Fit Group To Contents',
      versionAdded: '1.4.9',
      function: () => {
        for (const group of app.canvas.selectedItems) {
          if (group instanceof LGraphGroup) {
            group.recomputeInsideNodes()
            const padding = useSettingStore().get(
              'Comfy.GroupSelectedNodes.Padding'
            )
            group.resizeTo(group.children, padding)
            app.graph.change()
          }
        }
      }
    },
    {
      id: 'Comfy.Help.OpenComfyUIIssues',
      icon: 'pi pi-github',
      label: 'Open ComfyUI Issues',
      menubarLabel: 'ComfyUI Issues',
      versionAdded: '1.5.5',
      function: () => {
        telemetry?.trackHelpResourceClicked({
          resource_type: 'github',
          is_external: true,
          source: 'menu'
        })
        window.open(staticUrls.githubIssues, '_blank')
      }
    },
    {
      id: 'Comfy.Help.OpenComfyUIDocs',
      icon: 'pi pi-info-circle',
      label: 'Open ComfyUI Docs',
      menubarLabel: 'ComfyUI Docs',
      versionAdded: '1.5.5',
      function: () => {
        telemetry?.trackHelpResourceClicked({
          resource_type: 'docs',
          is_external: true,
          source: 'menu'
        })
        window.open(buildDocsUrl('/', { includeLocale: true }), '_blank')
      }
    },
    {
      id: 'Comfy.Help.OpenComfyOrgDiscord',
      icon: 'pi pi-discord',
      label: 'Open Comfy-Org Discord',
      menubarLabel: 'Comfy-Org Discord',
      versionAdded: '1.5.5',
      function: () => {
        telemetry?.trackHelpResourceClicked({
          resource_type: 'discord',
          is_external: true,
          source: 'menu'
        })
        window.open(staticUrls.discord, '_blank')
      }
    },
    {
      id: 'Workspace.SearchBox.Toggle',
      icon: 'pi pi-search',
      label: 'Toggle Search Box',
      versionAdded: '1.5.7',
      function: () => {
        useSearchBoxStore().toggleVisible()
      }
    },
    {
      id: 'Comfy.Help.AboutComfyUI',
      icon: 'pi pi-info-circle',
      label: 'Open About ComfyUI',
      menubarLabel: 'About ComfyUI',
      versionAdded: '1.6.4',
      function: () => {
        dialogService.showSettingsDialog('about')
      }
    },
    {
      id: 'Comfy.DuplicateWorkflow',
      icon: 'pi pi-clone',
      label: 'Duplicate Current Workflow',
      versionAdded: '1.6.15',
      function: async () => {
        await workflowService.duplicateWorkflow(workflowStore.activeWorkflow!)
      }
    },
    {
      id: 'Workspace.CloseWorkflow',
      icon: 'pi pi-times',
      label: 'Close Current Workflow',
      versionAdded: '1.7.3',
      function: async () => {
        if (workflowStore.activeWorkflow)
          await workflowService.closeWorkflow(workflowStore.activeWorkflow)
      }
    },
    {
      id: 'Comfy.ContactSupport',
      icon: 'pi pi-question',
      label: 'Contact Support',
      versionAdded: '1.17.8',
      function: () => {
        const { userEmail, resolvedUserInfo } = useCurrentUser()
        const supportUrl = buildSupportUrl({
          userEmail: userEmail.value,
          userId: resolvedUserInfo.value?.id
        })
        window.open(supportUrl, '_blank')
      }
    },
    {
      id: 'Comfy.Help.OpenComfyUIForum',
      icon: 'pi pi-comments',
      label: 'Open ComfyUI Forum',
      menubarLabel: 'ComfyUI Forum',
      versionAdded: '1.8.2',
      function: () => {
        telemetry?.trackHelpResourceClicked({
          resource_type: 'help_feedback',
          is_external: true,
          source: 'menu'
        })
        window.open(staticUrls.forum, '_blank')
      }
    },
    {
      id: 'Comfy.Canvas.DeleteSelectedItems',
      icon: 'pi pi-trash',
      label: 'Delete Selected Items',
      versionAdded: '1.10.5',
      function: () => {
        app.canvas.deleteSelected()
        app.canvas.setDirty(true, true)
      }
    },
    {
      id: 'Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu',
      icon: 'pi pi-puzzle',
      label: 'Custom Nodes Manager',
      versionAdded: '1.12.10',
      function: async () => {
        await useManagerState().openManager({
          showToastOnLegacyError: true
        })
      }
    },
    {
      id: 'Comfy.Manager.ShowUpdateAvailablePacks',
      icon: 'pi pi-sync',
      label: 'Check for Custom Node Updates',
      versionAdded: '1.17.0',
      function: async () => {
        const managerState = useManagerState()
        const state = managerState.managerUIState.value

        // For DISABLED state, show error toast instead of opening settings
        if (state === ManagerUIState.DISABLED) {
          toastStore.add({
            severity: 'error',
            summary: t('g.error'),
            detail: t('manager.notAvailable'),
            life: 3000
          })
          return
        }

        await managerState.openManager({
          initialTab: ManagerTab.UpdateAvailable,
          showToastOnLegacyError: false
        })
      }
    },
    {
      id: 'Comfy.Manager.ShowMissingPacks',
      icon: 'pi pi-exclamation-circle',
      label: 'Install Missing Custom Nodes',
      versionAdded: '1.17.0',
      function: async () => {
        await useManagerState().openManager({
          initialTab: ManagerTab.Missing,
          showToastOnLegacyError: false
        })
      }
    },
    {
      id: 'Comfy.Manager.ToggleManagerProgressDialog',
      icon: 'pi pi-spinner',
      label: 'Toggle the Custom Nodes Manager Progress Bar',
      versionAdded: '1.13.9',
      function: () => {
        dialogService.toggleManagerProgressDialog()
      }
    },
    {
      id: 'Comfy.User.OpenSignInDialog',
      icon: 'pi pi-user',
      label: 'Open Sign In Dialog',
      versionAdded: '1.17.6',
      function: async () => {
        await dialogService.showSignInDialog()
      }
    },
    {
      id: 'Comfy.User.SignOut',
      icon: 'pi pi-sign-out',
      label: 'Sign Out',
      versionAdded: '1.18.1',
      function: async () => {
        await firebaseAuthActions.logout()
      }
    },
    {
      id: 'Comfy.Canvas.MoveSelectedNodes.Up',
      icon: 'pi pi-arrow-up',
      label: 'Move Selected Nodes Up',
      versionAdded: moveSelectedNodesVersionAdded,
      function: () => moveSelectedNodes(([x, y], gridSize) => [x, y - gridSize])
    },
    {
      id: 'Comfy.Canvas.MoveSelectedNodes.Down',
      icon: 'pi pi-arrow-down',
      label: 'Move Selected Nodes Down',
      versionAdded: moveSelectedNodesVersionAdded,
      function: () => moveSelectedNodes(([x, y], gridSize) => [x, y + gridSize])
    },
    {
      id: 'Comfy.Canvas.MoveSelectedNodes.Left',
      icon: 'pi pi-arrow-left',
      label: 'Move Selected Nodes Left',
      versionAdded: moveSelectedNodesVersionAdded,
      function: () => moveSelectedNodes(([x, y], gridSize) => [x - gridSize, y])
    },
    {
      id: 'Comfy.Canvas.MoveSelectedNodes.Right',
      icon: 'pi pi-arrow-right',
      label: 'Move Selected Nodes Right',
      versionAdded: moveSelectedNodesVersionAdded,
      function: () => moveSelectedNodes(([x, y], gridSize) => [x + gridSize, y])
    },
    {
      id: 'Comfy.Graph.ConvertToSubgraph',
      icon: 'icon-[lucide--shrink]',
      label: 'Convert Selection to Subgraph',
      versionAdded: '1.20.1',
      category: 'essentials' as const,
      function: () => {
        const canvas = canvasStore.getCanvas()
        const graph = canvas.subgraph ?? canvas.graph
        if (!graph) throw new TypeError('Canvas has no graph or subgraph set.')

        const res = graph.convertToSubgraph(canvas.selectedItems)
        if (!res) {
          toastStore.add({
            severity: 'error',
            summary: t('toastMessages.cannotCreateSubgraph'),
            detail: t('toastMessages.failedToConvertToSubgraph'),
            life: 3000
          })
          return
        }

        const { node } = res
        canvas.select(node)
        canvasStore.updateSelectedItems()
      }
    },
    {
      id: 'Comfy.Graph.UnpackSubgraph',
      icon: 'icon-[lucide--expand]',
      label: 'Unpack the selected Subgraph',
      versionAdded: '1.26.3',
      function: () => {
        const canvas = canvasStore.getCanvas()
        const graph = canvas.subgraph ?? canvas.graph
        if (!graph) throw new TypeError('Canvas has no graph or subgraph set.')

        const subgraphNode = app.canvas.selectedItems.values().next().value
        if (!(subgraphNode instanceof SubgraphNode)) return
        useNodeOutputStore().revokeSubgraphPreviews(subgraphNode)
        graph.unpackSubgraph(subgraphNode)
      }
    },
    {
      id: 'Comfy.Graph.EditSubgraphWidgets',
      label: 'Edit Subgraph Widgets',
      icon: 'icon-[lucide--settings-2]',
      versionAdded: '1.28.5',
      function: () => {
        useRightSidePanelStore().openPanel('subgraph')
      }
    },
    {
      id: 'Comfy.Graph.ToggleWidgetPromotion',
      icon: 'icon-[lucide--arrow-left-right]',
      label: 'Toggle promotion of hovered widget',
      versionAdded: '1.30.1',
      function: tryToggleWidgetPromotion
    },
    {
      id: 'Comfy.OpenManagerDialog',
      icon: 'mdi mdi-puzzle-outline',
      label: 'Manager',
      function: async () => {
        await useManagerState().openManager({
          initialTab: ManagerTab.All,
          showToastOnLegacyError: false
        })
      }
    },
    {
      id: 'Comfy.ToggleHelpCenter',
      icon: 'pi pi-question-circle',
      label: 'Help Center',
      function: () => {
        useHelpCenterStore().toggle()
      },
      active: () => useHelpCenterStore().isVisible
    },
    {
      id: 'Comfy.ToggleCanvasInfo',
      icon: 'pi pi-info-circle',
      label: 'Canvas Performance',
      function: async () => {
        const settingStore = useSettingStore()
        const currentValue = settingStore.get('Comfy.Graph.CanvasInfo')
        await settingStore.set('Comfy.Graph.CanvasInfo', !currentValue)
      },
      active: () => useSettingStore().get('Comfy.Graph.CanvasInfo')
    },
    {
      id: 'Workspace.ToggleBottomPanel.Shortcuts',
      icon: 'pi pi-key',
      label: 'Show Keybindings Dialog',
      versionAdded: '1.24.1',
      category: 'view-controls' as const,
      function: () => {
        bottomPanelStore.togglePanel('shortcuts')
      }
    },
    {
      id: 'Comfy.Graph.ExitSubgraph',
      icon: 'pi pi-arrow-up',
      label: 'Exit Subgraph',
      versionAdded: '1.20.1',
      function: () => {
        const canvas = useCanvasStore().getCanvas()
        const navigationStore = useSubgraphNavigationStore()
        if (!canvas.graph) return

        canvas.setGraph(
          navigationStore.navigationStack.at(-2) ?? canvas.graph.rootGraph
        )
      }
    },
    {
      id: 'Comfy.Dev.ShowModelSelector',
      icon: 'pi pi-box',
      label: 'Show Model Selector (Dev)',
      versionAdded: '1.26.2',
      category: 'view-controls' as const,
      function: () => {
        const modelSelectorDialog = useModelSelectorDialog()
        modelSelectorDialog.show()
      }
    },
    {
      id: 'Comfy.Manager.CustomNodesManager.ShowLegacyCustomNodesMenu',
      icon: 'pi pi-bars',
      label: 'Custom Nodes (Legacy)',
      versionAdded: '1.16.4',
      function: async () => {
        await useManagerState().openManager({
          legacyCommand: 'Comfy.Manager.CustomNodesManager.ToggleVisibility',
          showToastOnLegacyError: true,
          isLegacyOnly: true
        })
      }
    },
    {
      id: 'Comfy.Manager.ShowLegacyManagerMenu',
      icon: 'mdi mdi-puzzle',
      label: 'Manager Menu (Legacy)',
      versionAdded: '1.16.4',
      function: async () => {
        await useManagerState().openManager({
          showToastOnLegacyError: true,
          isLegacyOnly: true
        })
      }
    },
    {
      id: 'Comfy.Memory.UnloadModels',
      icon: 'mdi mdi-vacuum-outline',
      label: 'Unload Models',
      versionAdded: '1.16.4',
      function: async () => {
        if (!useSettingStore().get('Comfy.Memory.AllowManualUnload')) {
          useToastStore().add({
            severity: 'error',
            summary: t('g.error'),
            detail: t('g.commandProhibited', {
              command: 'Comfy.Memory.UnloadModels'
            }),
            life: 3000
          })
          return
        }
        await api.freeMemory({ freeExecutionCache: false })
      }
    },
    {
      id: 'Comfy.Memory.UnloadModelsAndExecutionCache',
      icon: 'mdi mdi-vacuum-outline',
      label: 'Unload Models and Execution Cache',
      versionAdded: '1.16.4',
      function: async () => {
        if (!useSettingStore().get('Comfy.Memory.AllowManualUnload')) {
          useToastStore().add({
            severity: 'error',
            summary: t('g.error'),
            detail: t('g.commandProhibited', {
              command: 'Comfy.Memory.UnloadModelsAndExecutionCache'
            }),
            life: 3000
          })
          return
        }
        await api.freeMemory({ freeExecutionCache: true })
      }
    },
    {
      id: 'Comfy.BrowseModelAssets',
      icon: 'pi pi-folder-open',
      label: 'Experimental: Browse Model Assets',
      versionAdded: '1.28.3',
      function: async () => {
        if (!useSettingStore().get('Comfy.Assets.UseAssetAPI')) {
          const confirmed = await dialogService.confirm({
            title: 'Enable Asset API',
            message:
              'The Asset API is currently disabled. Would you like to enable it?',
            type: 'default'
          })

          if (!confirmed) return

          const settingStore = useSettingStore()
          await settingStore.set('Comfy.Assets.UseAssetAPI', true)
          await workflowService.reloadCurrentWorkflow()
        }
        const assetBrowserDialog = useAssetBrowserDialog()
        await assetBrowserDialog.browse({
          assetType: 'models',
          title: t('sideToolbar.modelLibrary'),
          onAssetSelected: (asset) => {
            const result = createModelNodeFromAsset(asset)
            if (!result.success) {
              toastStore.add({
                severity: 'error',
                summary: t('g.error'),
                detail: t('assetBrowser.failedToCreateNode')
              })
              console.error('Node creation failed:', result.error)
            }
          }
        })
      }
    },
    {
      id: 'Comfy.ToggleAssetAPI',
      icon: 'pi pi-database',
      label: () =>
        `Experimental: ${
          useSettingStore().get('Comfy.Assets.UseAssetAPI')
            ? 'Disable'
            : 'Enable'
        } AssetAPI`,
      function: async () => {
        const settingStore = useSettingStore()
        const current = settingStore.get('Comfy.Assets.UseAssetAPI') ?? false
        await settingStore.set('Comfy.Assets.UseAssetAPI', !current)
        await useWorkflowService().reloadCurrentWorkflow() // ensure changes take effect immediately
      }
    },
    {
      id: 'Comfy.ToggleLinear',
      icon: 'pi pi-database',
      label: 'toggle linear mode',
      function: () => (canvasStore.linearMode = !canvasStore.linearMode)
    }
  ]

  return commands.map((command) => ({ ...command, source: 'System' }))
}
